package org.example;

// 常用的位操作
public class BitOperation {
    static class Demo {
        // 1. 利用或操作 `|` 和空格将英文字符转换为小写
        // ('a' | ' ') = 'a';
        // ('A' | ' ') = 'a'

        // 2. 利用与操作 `&` 和下划线将英文字符转换为大写
        // ('b' & '_') = 'B'
        // ('B' & '_') = 'B'

        // 3. 利用异或操作 `^` 和空格进行英文字符大小写互换
        // ('d' ^ ' ') = 'D'
        // ('D' ^ ' ') = 'd'

        // 以上操作能够产生奇特效果的原因在于 ASCII 编码
        // ASCII 字符其实就是数字，恰巧空格和下划线对应的数字通过位运算就能改变大小写
        // 有兴趣的读者可以查 ASCII 码表自己算算，我就不展开讲了

        // 4. 不用临时变量交换两个数
        // int a = 1, b = 2;
        // a ^= b;
        // b ^= a;
        // a ^= b;
        // 现在 a = 2, b = 1

        // 5. 加一
        // int n = 1;
        // n = -~n;
        // 现在 n = 2

        // 6. 减一
        // int n = 2;
        // n = ~-n;
        // 现在 n = 1

        // 7. 判断两个数是否异号
        // int x = -1, y = 2;
        // boolean f = ((x ^ y) < 0); // true

        // int x = 3, y = 2;
        // boolean f = ((x ^ y) < 0); // false
    }

    // index & (arr.length - 1) 的运用
    public void demo2() {
        // 我在 单调栈解题套路 中介绍过环形数组，其实就是利用求模（余数）的方式让数组看起来头尾相接形成一个环形，永远都走不完：
        // int[] arr = {1,2,3,4};
        // int index = 0;
        // while (true) {
        //     // 在环形数组中转圈
        //     print(arr[index % arr.length]);
        //     index++;
        // }
        // 输出：1,2,3,4,1,2,3,4,1,2,3,4...

        // 但模运算 % 对计算机来说其实是一个比较昂贵的操作，所以我们可以用 & 运算来求余数：
    }

    // a ^ a = 0 的运用
    // 异或运算的性质需要我们牢记的
    // 一个数和它本身做异或运算结果为0，即 a ^ a = 0；一个数和 0 做异或运算的结果为它本身，即 a ^ 0 = a。

    // 136. 只出现一次的数字
    // 给你一个 非空 整数数组 nums ，除了某个元素只出现一次以外，其余每个元素均出现两次。找出那个只出现了一次的元素。
    static class SingleNumber {
        public int singleNumber(int[] nums) {
            int res = 0;
            for (int n : nums) {
                res ^= n;
            }
            return res;
        }
    }
}
